﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Runtime;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

using LowLevelGraphics;
using LowLevelGraphics.Filter;
using LowLevelGraphics.CustomControls;

using Scripter.Interpreter;
using ScriptCompiler2;

namespace ImageRecognition2.Forms
{
    /// <summary>
    /// Note: 
    /// 
    /// Regex expression:
    /// 
    /// 111111111111   2222222222222222222222222222223333333333333
    /// [a-zA-Z0-9_]+[ ]([a-zA-Z0-9._]+[ ]*,[ ]){1,3}[a-zA-Z0-9_]*
    /// 
    /// Note the numbers:
    /// 
    /// 1: there is the start, we have to specify a name of a filter like e.g. normalize
    /// 2: Specifies, that we have to add parameters. Each parameter might be built of
    ///    0 to n parameters: {1,3}
    /// 3: this is the last parameter or nothing
    /// 
    /// When it's done correctly, these classes will provide a way to use filters at runtime
    /// in the following way (e.g. Normalize Filter):
    /// 
    /// Normalize Normalization_Mode
    /// 
    /// How does this work?
    /// 
    /// This will be replaced internally to
    /// 
    /// new Normalize(Normalization_Mode_Variable).Execute(_bitmap);
    /// </summary>
    public partial class FormScript : Form
    {
        private Dictionary<string, IFilter> m_aFilter = new Dictionary<string, IFilter>();

        protected CReflectiveEvaluator m_ReflectiveEvaluator = new CReflectiveEvaluator();

        private static List<IFilter> aFilter = new List<IFilter>();
        private List<string> m_StringList = new List<string>();
        private RuntimeInterpreter runtimeInterpreter = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="FormScript"/> class.
        /// </summary>
        public FormScript()
        {
            InitializeComponent();
            propertyGridFilter.PropertyValueChanged += new PropertyValueChangedEventHandler(propertyGrid_PropertyValueChanged);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FormScript"/> class.
        /// </summary>
        /// <param name="_pictureBox">The _picture box.</param>
        public FormScript(SelectablePictureBox _pictureBox)
        {
            InitializeComponent();

            InitializeCompiler();

            /*InitializeFilter(m_aFilter);
            foreach(KeyValuePair<IFilter,string> keyValuePair in m_aFilter)
            {
                comboBox1.Items.Add(keyValuePair.Key);
            }*/

            Initialize();
        }

        private void Initialize()
        {
            StreamReader reader = new StreamReader(ImageRecognition2Definitions.FilterTypeStringListFile);
            string[] aString = Regex.Split(reader.ReadToEnd(), Environment.NewLine);

            m_StringList.AddRange(aString);

            AutoCompleteStringCollection autoCompleteStringCollection = Definitions.GetAutoCompleteFromFilterList();
            comboBoxFilter.AutoCompleteCustomSource = autoCompleteStringCollection;
            comboBoxFilter.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
            comboBoxFilter.AutoCompleteSource = AutoCompleteSource.CustomSource;

            pictureBox1.BackgroundImage = MainFormSingleton.ActivePictureBox.BackgroundImage;
            pictureBox1.BackgroundImageLayout = ImageLayout.Zoom;
            foreach (object type in Enum.GetValues(typeof(PROCESSING_MODE)))
            {
                cobProcessingMode.Items.Add(type);
            }
            cobProcessingMode.SelectedItem = PROCESSING_MODE.Color;
        }

        /// <summary>
        /// Initializes the compiler.
        /// </summary>
        private void InitializeCompiler()
        {
            string sLowLevelGraphicsPath = Application.StartupPath + @"\LowLevelGraphics.dll";
            m_ReflectiveEvaluator.AddUsing("System.Windows.Forms.dll");
            m_ReflectiveEvaluator.AddUsing("System.Drawing.dll");
            m_ReflectiveEvaluator.AddUsing(sLowLevelGraphicsPath);

            m_ReflectiveEvaluator.CompilerSuccess += new EventHandler(m_ReflectiveEvaluator_CompilerSuccess);
            m_ReflectiveEvaluator.CompilerError += new CompilerErrorEventHandler(m_ReflectiveEvaluator_CompilerError);
            m_ReflectiveEvaluator.TypeCreationError += new TypeCreationErrorEventHandler(m_ReflectiveEvaluator_TypeCreationError);
        }

        /// <summary>
        /// Type creation error
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_ReflectiveEvaluator_TypeCreationError(object sender, TypeCreationErrorEventArgs e)
        {
            MessageBox.Show(string.Format("Der Typ '{0}' konnte nicht erstellt werden.", e.TypeName));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_ReflectiveEvaluator_CompilerError(object sender, CompilerErrorEventArgs e)
        {
            //MessageBox.Show(e.Source + Environment.NewLine + " " + e.CompilerErrorCollection[0].ErrorText);
            Debug.WriteLine(e.Source + Environment.NewLine + e.CompilerErrorCollection[0].ErrorText);
        }

        /// <summary>
        /// Handles the CompilerSuccess event of the m_ReflectiveEvaluator control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void m_ReflectiveEvaluator_CompilerSuccess(object sender, EventArgs e)
        {
            Debug.WriteLine("Successfull!");
        }

        /// <summary>
        /// Execute Compiled Script
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbExecute_Click(object sender, EventArgs e)
        {
            Execute();
        }

        private void Execute()
        {
            string[] aLine = textBox1.Lines;

            if (MainFormSingleton.ActivePictureBox == null) return;

            Bitmap bitmap = null;
            if (chkbShowLive.Checked)
            {
                bitmap = (Bitmap)MainFormSingleton.ActivePictureBox.BackgroundImage.Clone();
                this.tbScript.Text = Helper.GetInsertStatementFromFilter((IFilter)propertyGridFilter.SelectedObject);
            }
            else
            {
                bitmap = (Bitmap)MainFormSingleton.ActivePictureBox.BackgroundImage;
            }

            UnsafeBitmap newUnsafeBitmap = null;
            if (radCompile.Checked)
            {
                newUnsafeBitmap = ExecuteCompiledScript(bitmap);
            }
            else
            {
                newUnsafeBitmap = ExecuteInterpretedScript(bitmap);
            }
            newUnsafeBitmap.Dispose();

            if (chkbShowLive.Checked)
            {
                MainFormSingleton.ActivePictureBox.BackgroundImage = newUnsafeBitmap.InternalBitmap;
            }
            MainFormSingleton.ActivePictureBox.Invalidate();
        }

        /// <summary>
        /// Executes the compiled script.
        /// </summary>
        /// <param name="bitmap">The bitmap.</param>
        /// <returns></returns>
        private UnsafeBitmap ExecuteCompiledScript(Bitmap bitmap)
        {
            string sScript = Definitions.ScripterScript;
            sScript = sScript.Replace("//{0}", textBox1.Text);

            UnsafeBitmap newUnsafeBitmap = new UnsafeBitmap(bitmap);
            m_ReflectiveEvaluator.Eval(sScript, "ScripterScript", "Execute", newUnsafeBitmap);
            return newUnsafeBitmap;
        }

        private UnsafeBitmap ExecuteInterpretedScript(Bitmap bitmap, string _sScriptToInterpret = null)
        {
            string sSource = string.Empty;

            foreach (object oLine in this.tbScript.Lines)
            {
                sSource += oLine.ToString() + Environment.NewLine;
            }

            string sLibraryPath = Application.StartupPath + @"\LowLevelGraphics.dll";

            Assembly assembly = Assembly.LoadFrom(sLibraryPath);
            runtimeInterpreter = new RuntimeInterpreter(sSource, assembly);
            runtimeInterpreter.Bitmap = MainFormSingleton.ActivePictureBox.BackgroundImage;
            runtimeInterpreter.Interpret();

            return runtimeInterpreter.Bitmap;
        }

        /// <summary>
        /// Handles the Click event of the cbAdd control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cbAdd_Click(object sender, EventArgs e)
        {
            AddFilter();
        }

        /// <summary>
        /// Adds this filter...
        /// </summary>
        private void AddFilter()
        {
            IFilter filter = GetFilter(comboBoxFilter.Text);   //GetFilter(sLine);
            if (filter != null)
            {
                aFilter.Add(filter);
                this.textBox1.AppendText(filter.ToString());
                this.textBox1.AppendText(Environment.NewLine);
            }
            else
            {
                Debug.WriteLine(string.Format("Filter {0} not added", comboBoxFilter.Text));
            }
        }

        /// <summary>
        /// Delete
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbDelete_Click(object sender, EventArgs e)
        {
            this.textBox1.Clear();
        }

        /// <summary>
        /// Handles the Click event of the cbPropertyGrid control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cbPropertyGrid_Click(object sender, EventArgs e)
        {
            ShowFilterProperties();
        }

        /// <summary>
        /// Gets the filter
        /// </summary>
        /// <param name="_sText"></param>
        /// <returns></returns>
        private IFilter GetFilter(string _sText)
        {
            string sString = string.Empty;
            IFilter filter = null;
            if (m_aFilter.ContainsKey(_sText))
            {
                filter = m_aFilter[_sText];
            }
            else
            {
                List<string> aString = m_StringList.FindAll(delegate(string _sString) { return _sString.ToLower().Contains(_sText.ToLower()); });
                foreach (string sTempString in aString)
                {
                    if (sTempString.EndsWith("." + _sText))
                    {
                        sString = sTempString;
                        break;
                    }
                }

                if (sString.Length == 0)
                {
                    Debug.WriteLine(string.Format("Filter {0} not found", _sText));
                    return null;
                }
            }
            if (MainFormSingleton.ActivePictureBox == null) return null;

            Bitmap bitmap = (Bitmap)MainFormSingleton.ActivePictureBox.BackgroundImage;
            if (filter == null)
            {
                filter = (IFilter)Activator.CreateInstance(Definitions.Assembly.GetName().FullName, sString).Unwrap();//GetFilter(sLine);
            }
            if (!m_aFilter.ContainsKey(_sText)) m_aFilter.Add(_sText, filter);
            return filter;
        }

        /// <summary>
        /// show values of the filter
        /// </summary>
        private void ShowFilterProperties()
        {
            propertyGridFilter.SelectedObject = GetFilter(comboBoxFilter.Text);//GetFilter(sLine);
        }

        /// <summary>
        /// Immediately execute filter on changed value in propertygrid
        /// </summary>
        /// <param name="s"></param>
        /// <param name="e"></param>
        void propertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            IFilter filter = GetPropertyGridSelectedFilter(s);
            filter.Execute(MainFormSingleton.ActivePictureBox.BackgroundImage).Dispose();
            MainFormSingleton.ActivePictureBox.Invalidate();
        }

        private static IFilter GetPropertyGridSelectedFilter(object s)
        {
            return ((IFilter)(((PropertyGrid)s).SelectedObject));
        }

        /// <summary>
        /// Handles the Click event of the cbExecuteSelectedFilter control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void cbExecuteSelectedFilter_Click(object sender, EventArgs e)
        {
            ExecuteSelectedFilter(comboBoxFilter.Text);
        }

        /// <summary>
        /// Executes the selected filter
        /// </summary>
        /// <param name="_sText"></param>
        private void ExecuteSelectedFilter(string _sText)
        {
            string sTextBoxText = _sText;

            if (MainFormSingleton.ActivePictureBox == null) return;

            Bitmap bitmap = (Bitmap)MainFormSingleton.ActivePictureBox.BackgroundImage;

            IFilter filter = GetFilter(_sText);
            if (filter != null)
            {
                bool bAtLeastOneRGBChannelChecked = chkbA.Checked || chkbR.Checked || chkbG.Checked || chkbA.Checked;
                bool bMaskedChecked = chkbMask.Checked;
                if (bMaskedChecked)
                {
                    FormChildList formChildList = new FormChildList();
                    if (formChildList.ShowDialog() != DialogResult.OK) return;
                    FormItem item = formChildList.FormItem;
                    if (item == null) return;

                    Image image = item.Form.pictureBox1.BackgroundImage;
                    UnsafeBitmap bitmapMask = image;

                    filter.ExecutedMaskedChannelled(bitmap, bitmapMask, chkbA.Checked, chkbR.Checked, chkbG.Checked, chkB.Checked);
                    bitmapMask.UnlockImage();
                }
                else if (bAtLeastOneRGBChannelChecked)
                {
                    filter.ExecuteChannelled(bitmap, chkbA.Checked, chkbR.Checked, chkbG.Checked, chkB.Checked);
                }
                else
                {
                    filter.Execute(bitmap).Dispose();
                }
                Debug.WriteLine(filter.ToString());
            }
            filter = null;
            MainFormSingleton.ActivePictureBox.Invalidate();
        }

        /// <summary>
        /// Handles the KeyDown event of the comboBox1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void comboBoxFilter_KeyDown(object sender, KeyEventArgs e)
        {
            if (!chkbExecuteOnReturn.Checked) return;
            if (e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter)
            {
                ExecuteSelectedFilter(comboBoxFilter.Text);
            }
        }

        /// <summary>
        /// Execute added filter
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbExecuteAddedFilters_Click(object sender, EventArgs e)
        {
            ExecuteAddedFilters();
        }

        /// <summary>
        /// Execute added filters
        /// </summary>
        private void ExecuteAddedFilters()
        {
            foreach (IFilter filter in aFilter)
            {
                Bitmap bitmap = (Bitmap)MainFormSingleton.ActivePictureBox.BackgroundImage;
                filter.Execute(bitmap).Dispose();
            }
            MainFormSingleton.ActivePictureBox.Invalidate();
        }

        private void cbInsert_Click(object sender, EventArgs e)
        {
            InsertStatement();
        }

        private void InsertStatement()
        {
            IFilter filter = (IFilter)propertyGridFilter.SelectedObject;
            if (filter == null) return;
            string sCommandString = Helper.GetInsertStatementFromFilter(filter);
            this.tbScript.AppendText(sCommandString);
        }

        private void comboBoxFilter_TextChanged(object sender, EventArgs e)
        {
            propertyGridFilter.SelectedObject = GetFilter(comboBoxFilter.Text);
        }

        private void cbCreateSyntaxString_Click(object sender, EventArgs e)
        {
            IFilter filter = GetFilter(comboBoxFilter.Text);   //GetFilter(sLine);
            string sSyntax = Helper.CreateSyntaxString(filter.GetType());
            MessageBox.Show(sSyntax);
        }

        private void propertyGridFilter_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            IFilter filter = null;

            if (!chkbShowLive.Checked) return;

            if (radDirect.Checked)
            {
                filter = (IFilter)propertyGridFilter.SelectedObject;
                UnsafeBitmap bitmap = (Bitmap)MainFormSingleton.ActivePictureBox.BackgroundImage;
                bitmap = filter.ExecuteCloned(bitmap);
                MainFormSingleton.ActivePictureBox.BackgroundImage = bitmap.InternalBitmap;
                MainFormSingleton.ActivePictureBox.Invalidate();
                bitmap.Dispose();
                return;
            }



            filter = (IFilter)propertyGridFilter.SelectedObject;
            if (filter == null) return;
            string sInsertStatement = Helper.GetInsertStatementFromFilter(filter);
            Execute();
        }
    }
}
